package pers.mihao.quickstream.common.util;

import pers.mihao.quickstream.common.exception.BaseException;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.io.*;
import java.util.Collection;
import java.util.function.Function;

/**
 * @author mihao
 * @since 2021/1/18 10:03
 */
public class ImgUtil {

    /**
     * 替换一个文件的所有要素点 替换成另一个颜色 并保存文件
     *
     * @param sourceColors
     * @param newColorList
     * @param sourceFile
     * @param newFilePath
     */
    public static void replaceColor(Collection<String> sourceColors, Collection<String> newColorList,
        File sourceFile, String newFilePath) throws IOException {

        if (sourceColors.size() != newColorList.size()) {
            throw new BaseException("需要替换颜色数量不新颜色数量不一致");
        }
        String[] sourceColorArray = new String[sourceColors.size()], newColorArray = new String[sourceColors.size()];
        sourceColors.toArray(sourceColorArray);
        newColorList.toArray(newColorArray);

        String oldColor, newColor;
        BufferedImage bi = ImageIO.read(sourceFile);
        int width = bi.getWidth(), height = bi.getHeight(), minx = bi.getMinX(), miny = bi.getMinY();

        Color color;
        int[] newColorRgb, rgb;
        for (int k = 0; k < sourceColorArray.length; k++) {
            oldColor = sourceColorArray[k];
            newColor = newColorArray[k];
            newColorRgb = hex2Rgb(newColor);
            color = new Color(newColorRgb[0], newColorRgb[1], newColorRgb[2]);
            rgb = new int[3];

            for (int i = minx; i < width; i++) {
                for (int j = miny; j < height; j++) {
                    int pixel = bi.getRGB(i, j);
                    // 下面三行代码将一个数字转换为RGB数字
                    rgb[0] = (pixel & 0xff0000) >> 16;
                    rgb[1] = (pixel & 0xff00) >> 8;
                    rgb[2] = (pixel & 0xff);
                    if (oldColor.equalsIgnoreCase(rgb2Hex(rgb[0], rgb[1], rgb[2]))) {
                        bi.setRGB(i, j, color.getRGB());
                    }
                }
            }
        }
        File file = FileUtil.newFile(newFilePath);
        if (file.exists()) {
            file.delete();
            file.createNewFile();
        }
        ImageIO.write(bi, "PNG", file);
        bi.flush();
    }

    /**
     * 16进制颜色字符串转换成rgb
     *
     * @param hexStr
     * @return rgb
     */
    public static int[] hex2Rgb(String hexStr) {
        if (StringUtil.isNotBlack(hexStr) && hexStr.length() == 7) {
            int[] rgb = new int[3];
            rgb[0] = Integer.valueOf(hexStr.substring(1, 3), 16);
            rgb[1] = Integer.valueOf(hexStr.substring(3, 5), 16);
            rgb[2] = Integer.valueOf(hexStr.substring(5, 7), 16);
            return rgb;
        }
        throw new BaseException("颜色格式错误");
    }

    /**
     * 将图片改成黑白的
     *
     * @param img
     * @param newFile
     */
    public static void changeImg2Bw(File img, String newFile) throws IOException {
        BufferedImage image = ImageIO.read(img);
        int srcH = image.getHeight(), srcW = image.getWidth();
        int minx = image.getMinX(), miny = image.getMinY();
        // 记录透明的位置
        int[][] alphaArray = new int[srcW][srcH];
        for (int i = minx; i < srcW; i++) {
            for (int j = miny; j < srcH; j++) {
                if (((image.getRGB(i, j) >> 24) & 0xff) == 0) {
                    alphaArray[i][j] = 1;
                }
            }
        }

        BufferedImage bufferedImage = new BufferedImage(srcW, srcH, BufferedImage.TYPE_BYTE_GRAY);
        bufferedImage.getGraphics().drawImage(image, 0, 0, srcW, srcH, null);
        bufferedImage = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null)
            .filter(bufferedImage, null);

        File tempFile = new File(newFile);
        ImageIO.write(bufferedImage, "PNG", tempFile);
        bufferedImage.flush();

        changeAlpha(newFile, alphaArray);
    }

    /**
     * @param path       路径
     * @param alphaArray
     */
    private static void changeAlpha(String path, int[][] alphaArray) throws IOException {
        // 检查透明度是否越界
        BufferedImage image = ImageIO.read(new File(path));
        int weight = image.getWidth();
        int height = image.getHeight();

        BufferedImage output = new BufferedImage(weight, height, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = output.createGraphics();
        output = g2.getDeviceConfiguration().createCompatibleImage(weight, height, Transparency.TRANSLUCENT);
        g2.dispose();
        g2 = output.createGraphics();

        // 调制透明度
        int rgb;
        for (int i = output.getMinY(); i < output.getHeight(); i++) {
            for (int j = output.getMinX(); j < output.getWidth(); j++) {
                // 对当前颜色判断是否在指定区间内
                if (alphaArray[i][j] != 1) {
                    rgb = output.getRGB(i, j);
                    rgb = ((10 * 255 / 10) << 24) | (rgb & 0x00ffffff);
                    output.setRGB(i, j, rgb);
                }
            }
        }
        g2.setComposite(AlphaComposite.SrcIn);
        g2.drawImage(image, 0, 0, weight, height, null);
        g2.dispose();
        ImageIO.write(output, "png", new File(path));

    }

    public static void main(String[] args) throws IOException {

//        String url = "/Users/mihao/Desktop/temp/m120x120_2_new.png";
//        print(url);

        String url = "/Users/mihao/Desktop/temp/m120x120_2.png";
        setRGBToZero(url, i->{
            if (i >= -2820000 && i < -1450000) {
                return true;
            }
            return false;
        });
    }

    private static void print(String url) throws IOException {
        BufferedImage image = ImageIO.read(new File(url));
        for (int i = 0; i < image.getWidth(); i++) {
            System.out.print("[");
            for (int j = 0; j < image.getHeight(); j++) {
                System.out.print(image.getRGB(i, j) + ", ");
            }
            System.out.print("]\n");
        }
    }

    private static String setRGBToZero(String file, Function<Integer, Boolean> isChange) throws IOException {
        File file1 = new File(file);
        BufferedImage image = ImageIO.read(file1);
        for (int i = 0; i < image.getWidth(); i++) {
            for (int j = 0; j < image.getHeight(); j++) {
                if (isChange.apply(image.getRGB(i, j))) {
                    image.setRGB(i, j, 0);
                }
            }
        }
        String newFile = file1.getParent() + File.separator + FileUtil.getFileRealName(file) + "_new." + FileUtil.getFileSuffix(file);
        ImageIO.write(image, FileUtil.getFileSuffix(file), new File(newFile));
        return newFile;
    }

    /**
     * 裁剪图拍呢
     * @param x
     * @param y
     * @param targetWidth
     * @param targetHeight
     * @param srcPath
     * @param targetPath
     * @throws IOException
     */
    public static void cutoutAndMin(int x, int y, int targetWidth, int targetHeight, String srcPath,
                                     String targetPath) throws IOException {

        cutout(x, y, targetWidth, targetHeight, srcPath, targetPath, ImgUtil::madeMid);
    }

    public static BufferedImage madeMid(BufferedImage bufferedImage) {
        int width = bufferedImage.getWidth();
        int height = bufferedImage.getHeight();
        int leftBlackNum = 0, rightBlackNum = 0;
        int leftStart = 0;
        for (int i = leftStart; i < width; i++) {
            for (int j = 0; j < height; j++) {
                if (bufferedImage.getRGB(i, j) != 0) {
                    break;
                }
            }
            leftBlackNum ++;
        }

        int rightStart = width - 1;
        for (int i = rightStart; i >= 0; i--) {
            for (int j = 0; j < height; j++) {
                if (bufferedImage.getRGB(i, j) != 0) {
                    break;
                }
            }
            rightBlackNum ++;
        }

        if (leftBlackNum != rightBlackNum) {

        }

        return bufferedImage;

    }

    public static void cutout(int x, int y, int targetWidth, int targetHeight, String srcPath,
                              String targetPath) throws IOException {

        cutout(x, y, targetWidth, targetHeight, srcPath, targetPath, t -> t);
    }


    public static void cutout(int x, int y, int targetWidth, int targetHeight, String srcPath,
                              String targetPath, Function<BufferedImage, BufferedImage> function) throws IOException {
        FileInputStream fileInputStream = null;
        ImageInputStream imageInputStream = null;
        try {
            // 读取原始图片
            fileInputStream = new FileInputStream(srcPath);
            // ImageReader解码指定格式
            ImageReader imageReader = ImageIO.getImageReadersByFormatName(srcPath.substring(srcPath.lastIndexOf(".") + 1)).next();
            // 获取图片流
            imageInputStream = ImageIO.createImageInputStream(fileInputStream);
            // 将imageInputStream标记为true(只向前搜索)意味着包含在输入源中的图像将只按顺序读取
            imageReader.setInput(imageInputStream, true);
            // 指定在输入时从 Java Image I/O框架的上下文流中转换一幅图像或一组图像的参数
            ImageReadParam imageReadParam = imageReader.getDefaultReadParam();
            BufferedImage temp = ImageIO.read(new File(srcPath));
            if (x < 0) {
                x = temp.getWidth() + x;
            }
            if (y < 0) {
                y = temp.getHeight() + y;
            }
            // 定义空间中的一个区域
            Rectangle rectangle = new Rectangle(x, y, targetWidth, targetHeight);
            // 提供一个 BufferedImage，将其用作解码像素数据的目标。
            imageReadParam.setSourceRegion(rectangle);
            // 读取索引imageIndex指定的对象
            BufferedImage bufferedImage = imageReader.read(0, imageReadParam);
            bufferedImage = function.apply(bufferedImage);
            // 保存新图片
            FileUtil.createDir(targetPath);
            ImageIO.write(bufferedImage, targetPath.substring(targetPath.lastIndexOf(".") + 1)
                    , new File(targetPath));
        } finally {
            if (fileInputStream != null) {
                fileInputStream.close();
            }
            if (imageInputStream != null) {
                imageInputStream.close();
            }
        }
    }

    /**
     * 功能描述：多个jpg文件合并成一个jpg到本地
     *
     * @author songchengye
     * @since 2022/4/7 下午2:26
     */
    public static BufferedImage jpgsToJpg(String newFileName, Pair<String, Boolean>[] oldFiles) {
        try {
            // 得到图片的二进制数据，以二进制封装得到数据，具有通用性
            BufferedImage[] bufferedImages = new BufferedImage[oldFiles.length];
            for (int i = 0; i < oldFiles.length; i++) {
                try (FileInputStream fileInputStream = new FileInputStream(oldFiles[i].getKey())) {
                    bufferedImages[i] = ImageIO.read(fileInputStream);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            boolean[] isHorizontal = new boolean[oldFiles.length];
            for (int i = 0; i < oldFiles.length; i++) {
                isHorizontal[i] = oldFiles[i].getValue();
            }
            // 合并多张图片为1张
            BufferedImage bufferedImage = mergeImage(isHorizontal, bufferedImages);
            saveImgFile(newFileName, bufferedImage);
            return bufferedImage;
        } catch (Exception e) {
            throw new BaseException(e);
        }
    }

    /**
     * 保存图片
     * @param newFileName
     * @param bufferedImage
     * @return
     */
    public static File saveImgFile(String newFileName, BufferedImage bufferedImage) {
        byte[] bytes;
        File jpgFile = null;
        //指定文件路径
        jpgFile = FileUtil.newFile(newFileName);
        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
             FileOutputStream outStream = new FileOutputStream(jpgFile)) {
            ImageIO.write(bufferedImage, FileUtil.getFileSuffix(newFileName), byteArrayOutputStream);
            bytes = byteArrayOutputStream.toByteArray();
            //写入数据
            outStream.write(bytes);
        } catch (IOException e) {
            throw new BaseException(e);
        }
        return jpgFile;
    }

    /**
     * 合成任意数量的图片
     *
     * @param isNewLine
     * @param bufferedImages
     * @return
     * @throws IOException
     */
    public static BufferedImage mergeImage(boolean[] isNewLine, BufferedImage[] bufferedImages) throws IOException {
        // 生成新图片
        BufferedImage destImage = null;
        // 计算新图片的长和高
        int allw = bufferedImages[0].getWidth(), allh = bufferedImages[0].getHeight();
        boolean newLine = false;
        for (int i = 1; i < bufferedImages.length; i++) {
            BufferedImage image = bufferedImages[i];
            if (isNewLine[i]) {
                newLine = true;
                allh += image.getHeight();
            }
            if (!newLine) {
                allw += image.getWidth();
            }

        }
        destImage = new BufferedImage(allw, allh, BufferedImage.TYPE_INT_RGB);
        // 合并所有子图片到新图片
        int wx = 0, wy = 0;
        for (int i = 0; i < bufferedImages.length; i++) {
            BufferedImage img = bufferedImages[i];
            int w1 = img.getWidth();
            int h1 = img.getHeight();
            // 从图片中读取RGB
            int[] imageArrayOne = new int[w1 * h1];
            imageArrayOne = img.getRGB(0, 0, w1, h1, imageArrayOne, 0, w1); // 逐行扫描图像中各个像素的RGB到数组中
            if (isNewLine[i] && i != 0) {
                // 垂直
                wx = 0;
                wy += h1;
                destImage.setRGB(wx, wy, w1, h1, imageArrayOne, 0, w1);
                wx += w1;
            } else {
                // 水平
                destImage.setRGB(wx, wy, w1, h1, imageArrayOne, 0, w1);
                wx += w1;
            }
        }
        return destImage;
    }



    /**
     * rgb转换成16进制
     *
     * @param r
     * @param g
     * @param b
     * @return
     */
    public static String rgb2Hex(int r, int g, int b) {
        return String.format("#%02X%02X%02X", r, g, b);
    }

}
